﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;

    /// <summary>
    /// 中国农历计算类
    /// 支持公历与农历的相互转换，干支纪年，生肖计算等
    /// 数据范围：1900年1月31日 - 2100年12月31日
    /// </summary>
    public class ChineseLunarCalendar
    {
        #region 常量定义

        // 基准日期：1900年1月31日（农历正月初一）
        private static readonly DateTime BaseDate = new DateTime(1900, 1, 31);

        // 农历数据表（1900-2100年）
        // 每个整数表示一年的农历信息，共32位：
        // 位31: 闰月天数（1:30天, 0:29天）
        // 位30-25: 闰月月份（0表示无闰月）
        // 位24-21: 12月天数（1:30天, 0:29天）
        // 位20-17: 11月天数，以此类推...
        private static readonly long[] LunarInfo =
        {
        0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
        0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,
        0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,
        0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,
        0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,
        0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0,
        0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0,
        0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6,
        0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570,
        0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0,
        0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,
        0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,
        0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530,
        0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,
        0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0,
        0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06aa0, 0x1a6c4, 0x0aae0,
        0x092e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4,
        0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0,
        0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160,
        0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252,
        0x0d520
    };

        // 天干名称
        private static readonly string[] TianGan = { "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸" };

        // 地支名称
        private static readonly string[] DiZhi = { "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥" };

        // 生肖名称
        private static readonly string[] Animals = { "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪" };

        // 农历月份名称
        private static readonly string[] LunarMonths = { "正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊" };

        // 农历日期名称
        private static readonly string[] LunarDays =
        {
        "初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十",
        "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十",
        "廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十"
    };

        // 二十四节气名称
        private static readonly string[] SolarTerms =
        {
        "小寒", "大寒", "立春", "雨水", "惊蛰", "春分", "清明", "谷雨",
        "立夏", "小满", "芒种", "夏至", "小暑", "大暑", "立秋", "处暑",
        "白露", "秋分", "寒露", "霜降", "立冬", "小雪", "大雪", "冬至"
    };

        #endregion

        #region 数据结构

        /// <summary>
        /// 农历日期信息
        /// </summary>
        public class LunarDate
        {
            /// <summary>农历年</summary>
            public int Year { get; set; }

            /// <summary>农历月</summary>
            public int Month { get; set; }

            /// <summary>农历日</summary>
            public int Day { get; set; }

            /// <summary>是否闰月</summary>
            public bool IsLeapMonth { get; set; }

            /// <summary>干支纪年</summary>
            public string GanZhiYear { get; set; }

            /// <summary>生肖</summary>
            public string Animal { get; set; }

            /// <summary>农历月份名称</summary>
            public string LunarMonthName { get; set; }

            /// <summary>农历日期名称</summary>
            public string LunarDayName { get; set; }

            /// <summary>节气名称（如为空则表示不是节气）</summary>
            public string SolarTerm { get; set; }

            /// <summary>节日名称（如为空则表示不是节日）</summary>
            public string Festival { get; set; }
        }

        #endregion

        #region 公共方法

        /// <summary>
        /// 将公历日期转换为农历日期
        /// </summary>
        /// <param name="solarDate">公历日期</param>
        /// <returns>农历日期信息</returns>
        public LunarDate ConvertToLunarDate(DateTime solarDate)
        {
            // 检查日期范围
            if (solarDate < BaseDate || solarDate > new DateTime(2100, 12, 31))
            {
                throw new ArgumentOutOfRangeException(nameof(solarDate), "日期必须在1900年1月31日至2100年12月31日之间");
            }

            // 计算与基准日期的天数差
            int totalDays = (solarDate - BaseDate).Days;

            int lunarYear = 0;
            int lunarMonth = 0;
            int lunarDay = 0;
            bool isLeapMonth = false;

            // 计算农历年
            lunarYear = CalculateLunarYear(totalDays, ref totalDays);

            // 计算农历月和日
            CalculateLunarMonthDay(lunarYear, totalDays, ref lunarMonth, ref lunarDay, ref isLeapMonth);

            // 获取节气信息
            string solarTerm = GetSolarTerm(solarDate);

            // 获取节日信息
            string festival = GetFestival(solarDate, lunarYear, lunarMonth, lunarDay, isLeapMonth);

            return new LunarDate
            {
                Year = lunarYear,
                Month = lunarMonth,
                Day = lunarDay,
                IsLeapMonth = isLeapMonth,
                GanZhiYear = GetGanZhiYear(lunarYear),
                Animal = GetAnimal(lunarYear),
                LunarMonthName = GetLunarMonthName(lunarMonth, isLeapMonth),
                LunarDayName = GetLunarDayName(lunarDay),
                SolarTerm = solarTerm,
                Festival = festival
            };
        }

        /// <summary>
        /// 将农历日期转换为公历日期
        /// </summary>
        /// <param name="lunarYear">农历年</param>
        /// <param name="lunarMonth">农历月</param>
        /// <param name="lunarDay">农历日</param>
        /// <param name="isLeapMonth">是否闰月</param>
        /// <returns>公历日期</returns>
        public DateTime ConvertToSolarDate(int lunarYear, int lunarMonth, int lunarDay, bool isLeapMonth)
        {
            // 检查农历日期范围
            if (lunarYear < 1900 || lunarYear > 2100)
                throw new ArgumentOutOfRangeException(nameof(lunarYear), "农历年必须在1900-2100之间");

            if (lunarMonth < 1 || lunarMonth > 12)
                throw new ArgumentOutOfRangeException(nameof(lunarMonth), "农历月必须在1-12之间");

            if (lunarDay < 1 || lunarDay > 30)
                throw new ArgumentOutOfRangeException(nameof(lunarDay), "农历日必须在1-30之间");

            // 计算总天数
            int totalDays = 0;

            // 累加从1900年到指定年份之前的所有天数
            for (int year = 1900; year < lunarYear; year++)
            {
                totalDays += GetLunarYearDays(year);
            }

            // 累加指定年份中，指定月份之前的所有月份天数
            int leapMonth = GetLeapMonth(lunarYear);
            for (int month = 1; month < lunarMonth; month++)
            {
                totalDays += GetLunarMonthDays(lunarYear, month);
            }

            // 处理闰月
            if (isLeapMonth)
            {
                if (leapMonth != lunarMonth)
                    throw new ArgumentException("指定的月份不是闰月");

                totalDays += GetLunarMonthDays(lunarYear, lunarMonth); // 加上闰月天数
            }
            else if (leapMonth > 0 && leapMonth < lunarMonth)
            {
                totalDays += GetLunarMonthDays(lunarYear, leapMonth); // 加上闰月天数
            }

            // 加上当月的天数
            totalDays += lunarDay - 1;

            return BaseDate.AddDays(totalDays);
        }

        /// <summary>
        /// 获取指定农历年的生肖
        /// </summary>
        public string GetAnimal(int lunarYear)
        {
            int index = (lunarYear - 4) % 12;
            if (index < 0) index += 12;
            return Animals[index];
        }

        /// <summary>
        /// 获取指定农历年的干支纪年
        /// </summary>
        public string GetGanZhiYear(int lunarYear)
        {
            int ganIndex = (lunarYear - 4) % 10;
            int zhiIndex = (lunarYear - 4) % 12;

            if (ganIndex < 0) ganIndex += 10;
            if (zhiIndex < 0) zhiIndex += 12;

            return TianGan[ganIndex] + DiZhi[zhiIndex];
        }

        /// <summary>
        /// 获取指定公历日期的节气
        /// </summary>
        public string GetSolarTerm(DateTime solarDate)
        {
            // 简化实现：使用固定日期表
            // 实际应用中应该使用精确的天文计算
            Dictionary<string, DateTime> solarTermTable = CreateSolarTermTable(solarDate.Year);

            foreach (var term in solarTermTable)
            {
                if (solarDate.Month == term.Value.Month && solarDate.Day == term.Value.Day)
                {
                    return term.Key;
                }
            }

            return string.Empty;
        }

        #endregion

        #region 核心计算方法

        /// <summary>
        /// 计算农历年
        /// </summary>
        /// <param name="totalDays">总天数（从基准日期开始）</param>
        /// <param name="remainingDays">剩余天数（输出参数）</param>
        /// <returns>农历年</returns>
        private int CalculateLunarYear(int totalDays, ref int remainingDays)
        {
            int year = 1900;
            int daysInYear = 0;

            // 逐年累加天数，直到找到对应的年份
            while (year <= 2100)
            {
                daysInYear = GetLunarYearDays(year);
                if (totalDays < daysInYear)
                    break;

                totalDays -= daysInYear;
                year++;
            }

            remainingDays = totalDays;
            return year;
        }

        /// <summary>
        /// 计算农历月份和日期
        /// </summary>
        private void CalculateLunarMonthDay(int lunarYear, int daysInYear, ref int month, ref int day, ref bool isLeapMonth)
        {
            int leapMonth = GetLeapMonth(lunarYear);
            int tempDays = 0;
            int monthDays = 0;

            isLeapMonth = false;
            month = 1;

            // 逐月累加天数，直到找到对应的月份
            while (month <= 12)
            {
                // 获取当前月的天数
                monthDays = GetLunarMonthDays(lunarYear, month);
                tempDays += monthDays;

                // 检查是否找到对应的月份
                if (daysInYear < tempDays)
                {
                    tempDays -= monthDays; // 回退天数
                    day = daysInYear - tempDays + 1;
                    return;
                }

                // 处理闰月
                if (leapMonth > 0 && month == leapMonth && !isLeapMonth)
                {
                    tempDays += GetLunarMonthDays(lunarYear, month); // 闰月天数
                    if (daysInYear < tempDays)
                    {
                        isLeapMonth = true;
                        month--; // 保持月份不变，但标记为闰月
                        day = daysInYear - (tempDays - GetLunarMonthDays(lunarYear, month)) + 1;
                        return;
                    }
                }

                month++;
            }

            // 如果到这里还没有返回，说明是下一年的正月初一
            month = 1;
            day = 1;
        }

        /// <summary>
        /// 获取指定农历年的总天数
        /// </summary>
        private int GetLunarYearDays(int lunarYear)
        {
            if (lunarYear < 1900 || lunarYear > 2100)
                return 0;

            long info = LunarInfo[lunarYear - 1900];
            int totalDays = 0;

            // 计算12个月的总天数
            for (int month = 1; month <= 12; month++)
            {
                totalDays += GetLunarMonthDays(lunarYear, month);
            }

            // 加上闰月天数（如果有的话）
            int leapMonth = GetLeapMonth(lunarYear);
            if (leapMonth > 0)
            {
                totalDays += GetLunarMonthDays(lunarYear, leapMonth);
            }

            return totalDays;
        }

        /// <summary>
        /// 获取指定农历年月的天数
        /// </summary>
        private int GetLunarMonthDays(int lunarYear, int lunarMonth)
        {
            if (lunarYear < 1900 || lunarYear > 2100)
                return 0;

            long info = LunarInfo[lunarYear - 1900];

            // 检查是否为闰月
            int leapMonth = GetLeapMonth(lunarYear);
            bool isLeap = (leapMonth == lunarMonth);

            if (isLeap)
            {
                // 闰月天数在最高位
                return ((info & 0x10000) != 0) ? 30 : 29;
            }
            else
            {
                // 正常月份天数
                int shift = 16 - lunarMonth; // 计算位移量
                if (shift < 1) shift = 1;
                return ((info & (1L << shift)) != 0) ? 30 : 29;
            }
        }

        /// <summary>
        /// 获取指定农历年的闰月（0表示无闰月）
        /// </summary>
        private int GetLeapMonth(int lunarYear)
        {
            if (lunarYear < 1900 || lunarYear > 2100)
                return 0;

            long info = LunarInfo[lunarYear - 1900];
            return (int)((info >> 16) & 0x1F);
        }

        #endregion

        #region 辅助方法

        /// <summary>
        /// 获取农历月份名称
        /// </summary>
        private string GetLunarMonthName(int month, bool isLeapMonth)
        {
            if (month < 1 || month > 12)
                return "未知月";

            string leapPrefix = isLeapMonth ? "闰" : "";
            return leapPrefix + LunarMonths[month - 1] + "月";
        }

        /// <summary>
        /// 获取农历日期名称
        /// </summary>
        private string GetLunarDayName(int day)
        {
            if (day < 1 || day > 30)
                return "未知日";

            return LunarDays[day - 1];
        }

        /// <summary>
        /// 创建节气表（简化版本）
        /// </summary>
        private Dictionary<string, DateTime> CreateSolarTermTable(int year)
        {
            // 这是一个简化的节气表，实际应该使用精确的天文计算
            // 这里使用近似的固定日期
            return new Dictionary<string, DateTime>
        {
            {"小寒", new DateTime(year, 1, 5)},
            {"大寒", new DateTime(year, 1, 20)},
            {"立春", new DateTime(year, 2, 4)},
            {"雨水", new DateTime(year, 2, 19)},
            {"惊蛰", new DateTime(year, 3, 5)},
            {"春分", new DateTime(year, 3, 20)},
            {"清明", new DateTime(year, 4, 4)},
            {"谷雨", new DateTime(year, 4, 20)},
            {"立夏", new DateTime(year, 5, 5)},
            {"小满", new DateTime(year, 5, 21)},
            {"芒种", new DateTime(year, 6, 6)},
            {"夏至", new DateTime(year, 6, 21)},
            {"小暑", new DateTime(year, 7, 7)},
            {"大暑", new DateTime(year, 7, 23)},
            {"立秋", new DateTime(year, 8, 7)},
            {"处暑", new DateTime(year, 8, 23)},
            {"白露", new DateTime(year, 9, 7)},
            {"秋分", new DateTime(year, 9, 23)},
            {"寒露", new DateTime(year, 10, 8)},
            {"霜降", new DateTime(year, 10, 23)},
            {"立冬", new DateTime(year, 11, 7)},
            {"小雪", new DateTime(year, 11, 22)},
            {"大雪", new DateTime(year, 12, 7)},
            {"冬至", new DateTime(year, 12, 22)}
        };
        }

        /// <summary>
        /// 获取节日信息
        /// </summary>
        private string GetFestival(DateTime solarDate, int lunarYear, int lunarMonth, int lunarDay, bool isLeapMonth)
        {
            // 公历节日
            if (solarDate.Month == 1 && solarDate.Day == 1) return "元旦";
            if (solarDate.Month == 2 && solarDate.Day == 14) return "情人节";
            if (solarDate.Month == 3 && solarDate.Day == 8) return "妇女节";
            if (solarDate.Month == 5 && solarDate.Day == 1) return "劳动节";
            if (solarDate.Month == 6 && solarDate.Day == 1) return "儿童节";
            if (solarDate.Month == 9 && solarDate.Day == 10) return "教师节";
            if (solarDate.Month == 10 && solarDate.Day == 1) return "国庆节";
            if (solarDate.Month == 12 && solarDate.Day == 25) return "圣诞节";

            // 农历节日（不考虑闰月）
            if (!isLeapMonth)
            {
                if (lunarMonth == 1 && lunarDay == 1) return "春节";
                if (lunarMonth == 1 && lunarDay == 15) return "元宵节";
                if (lunarMonth == 5 && lunarDay == 5) return "端午节";
                if (lunarMonth == 7 && lunarDay == 7) return "七夕";
                if (lunarMonth == 7 && lunarDay == 15) return "中元节";
                if (lunarMonth == 8 && lunarDay == 15) return "中秋节";
                if (lunarMonth == 9 && lunarDay == 9) return "重阳节";
                if (lunarMonth == 12 && lunarDay == 8) return "腊八节";
                if (lunarMonth == 12 && lunarDay == 23) return "小年";
                if (lunarMonth == 12 && lunarDay == 30) return "除夕";
            }

            return string.Empty;
        }

        #endregion

        #region 工具方法

        /// <summary>
        /// 获取农历日期字符串（格式：壬寅年腊月初八）
        /// </summary>
        public string GetLunarDateString(DateTime solarDate)
        {
            var lunarDate = ConvertToLunarDate(solarDate);
            return $"{lunarDate.GanZhiYear}年{lunarDate.LunarMonthName}{lunarDate.LunarDayName}";
        }

        /// <summary>
        /// 获取详细的农历信息字符串
        /// </summary>
        public string GetDetailedLunarInfo(DateTime solarDate)
        {
            var lunarDate = ConvertToLunarDate(solarDate);

            string info = $"{lunarDate.GanZhiYear}年({lunarDate.Animal}年){lunarDate.LunarMonthName}{lunarDate.LunarDayName}";

            if (!string.IsNullOrEmpty(lunarDate.SolarTerm))
                info += $" 【{lunarDate.SolarTerm}】";

            if (!string.IsNullOrEmpty(lunarDate.Festival))
                info += $" ★{lunarDate.Festival}★";

            return info;
        }

        /// <summary>
        /// 检查是否为传统节日
        /// </summary>
        public bool IsTraditionalFestival(DateTime solarDate)
        {
            var lunarDate = ConvertToLunarDate(solarDate);
            return !string.IsNullOrEmpty(lunarDate.Festival) &&
                   lunarDate.Festival != "元旦" &&
                   lunarDate.Festival != "情人节" &&
                   lunarDate.Festival != "圣诞节";
        }

        #endregion
    }

    // 使用示例类
    public class LunarCalendarDemo
    {
        public static void Demo()
        {
            ChineseLunarCalendar calendar = new ChineseLunarCalendar();
            // 示例1：将公历转换为农历
            DateTime solarDate =  DateTime.Now; // 2024年春节
            var lunarDate = calendar.ConvertToLunarDate(solarDate);

            Console.WriteLine($"公历: {solarDate:yyyy年MM月dd日}");
            Console.WriteLine($"农历: {lunarDate.LunarMonthName}{lunarDate.LunarDayName}");
            Console.WriteLine($"干支: {lunarDate.GanZhiYear}");
            Console.WriteLine($"生肖: {lunarDate.Animal}");
            Console.WriteLine($"节日: {lunarDate.Festival}");
            Console.WriteLine($"节气: {lunarDate.SolarTerm}");

            Console.WriteLine();

            // 示例2：将农历转换为公历
            DateTime convertedDate = calendar.ConvertToSolarDate(solarDate.Year, solarDate.Month, solarDate.Day, false);
            Console.WriteLine($"农历2024年正月初一对应的公历: {convertedDate:yyyy年MM月dd日}");

            Console.WriteLine();

            // 示例3：获取详细农历信息
            string detailedInfo = calendar.GetDetailedLunarInfo(DateTime.Today);
            Console.WriteLine($"今日农历: {detailedInfo}");

            Console.WriteLine();

            // 示例4：检查是否为传统节日
            bool isFestival = calendar.IsTraditionalFestival(DateTime.Now);
            Console.WriteLine($"2024年2月10日是否为传统节日: {isFestival}");
        }
    }
}
